package com.msh.frame.common.util;


import com.msh.frame.client.exception.BizException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;

/**
 * java使用AES加密解密 AES-128-ECB加密 与mysql数据库aes加密算法通用 数据库aes加密解密 -- 加密 SELECT
 * to_base64(AES_ENCRYPT('www.gowhere.so','jkl;POIU1234++==')); -- 解密 SELECT
 * AES_DECRYPT(from_base64('Oa1NPBSarXrPH8wqSRhh3g=='),'jkl;POIU1234++==');
 *
 * Cipher  AES加密为线程安全
 */
@Slf4j
public class AesUtil {

  public static AesInstance getInstance(String key) {
    return new AesInstance(key);
  }

  //线程安全测试方法
  public static void main(String[] args) throws InterruptedException {
    AesInstance instance = AesUtil.getInstance("1234567890123465");
    MultiThreadTestUtil.threadTest(instance, e -> {
      String str = String.valueOf(new Random().nextDouble());
      String s4 = instance.encrypt(str);
      String decrypt = instance.decrypt(s4);
      if (!str.equals(decrypt)) {
        System.out.println(str.equals(decrypt));
      }
    });
  }


  public static class AesInstance {

    private Cipher cipherE;
    private Cipher cipherD;

    private AesInstance(String key) {
      try {
        //1.构造密钥生成器，指定为AES算法,不区分大小写
        KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //2.根据ecnodeRules规则初始化密钥生成器
        //生成一个128位的随机源,根据传入的字节数组
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(key.getBytes(StandardCharsets.UTF_8));
        keygen.init(128, random);
        //3.产生原始对称密钥
        SecretKey originalKey = keygen.generateKey();
        //4.获得原始对称密钥的字节数组
        byte[] raw = originalKey.getEncoded();

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        //"算法/模式/补码方式"
        cipherD = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipherE = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipherD.init(Cipher.DECRYPT_MODE, skeySpec);
        cipherE.init(Cipher.ENCRYPT_MODE, skeySpec);
      } catch (NoSuchPaddingException e) {
        e.printStackTrace();
      } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
      } catch (InvalidKeyException e) {
        e.printStackTrace();
      }
    }

    /**
     * 加密
     */
    public byte[] encrypt(byte[] bs){
      try {
        return cipherE.doFinal(bs);
      } catch (Exception e) {
        log.error("加密失败", e);
        throw new BizException("AES加密失败");
      }
    }

    /**
     * 加密
     */
    public String encrypt(String src) {
      try {
        byte[] encrypted = cipherE.doFinal(src.getBytes("utf-8"));
        //此处使用BASE64做转码功能，同时能起到2次加密的作用。
        return Base64Util.encryptBuffer(encrypted);
      } catch (Exception e) {
        log.error("加密失败", e);
        throw new BizException("AES加密失败");
      }
    }

    /**
     * 解密
     */
    public byte[] decrypt(byte[] bs) {
      try {
        return  cipherD.doFinal(bs);
      } catch (Exception e) {
        log.error("解密失败", e);
        throw new BizException("AES解密失败");
      }
    }

    /**
     * 解密
     */
    public String decrypt(String src) {
      try {
        //先用base64解密
        byte[] encrypted = Base64Util.decodeBuffer(src);
        byte[] origina = cipherD.doFinal(encrypted);
        return new String(origina, "utf-8");
      } catch (Exception e) {
        log.error("解密失败", e);
        throw new BizException("AES解密失败");
      }
    }
  }

}
